package com.shujia

import org.junit.{Before, Test}

import scala.io.{BufferedSource, Source}

class Demo21StuExercise {
  case class Student(id: String, name: String, age: Int, gender: String, clazz: String)

  case class Score(id: String, subjectId: String, sco: Int)

  case class Subject(subjectId: String, subjectName: String, fullSco: Int)

  var student: List[Student] = _
  var score: List[Score] = _
  var subject: List[Subject] = _

  var sumScoreMap: Map[String, Int] = _
  var stuNameAndClazzMap: Map[String, String] = _
  var subjectFullScoMap: Map[String, Int] = _

  def readFile(path: String): List[String] = {
    val bs: BufferedSource = Source.fromFile(path)
    val list: List[String] = bs.getLines().toList

    bs.close()

    list
  }

  @Before
  def init(): Unit = {
    // 加载数据并将每条数据构建成样例类对象，最终形成一个List
    student = readFile("data/stu/students.txt")
      .map(line => {
        val splits: Array[String] = line.split(",")
        Student(splits(0), splits(1), splits(2).toInt, splits(3), splits(4))
      })

    score = readFile("data/stu/score.txt")
      .map(line => {
        val splits: Array[String] = line.split(",")
        Score(splits(0), splits(1), splits(2).toInt)
      })

    subject = readFile("data/stu/subject.txt")
      .map(line => {
        val splits: Array[String] = line.split(",")
        Subject(splits(0), splits(1), splits(2).toInt)
      })

    sumScoreMap = score.groupBy(_.id).map(kv => (kv._1, kv._2.map(_.sco).sum))
    stuNameAndClazzMap = student.map(stu => (stu.id, s"${stu.name},${stu.clazz}")).toMap
    // 以科目id作为key，科目总分作为value形成Map
    subjectFullScoMap = subject.map(s => (s.subjectId, s.fullSco)).toMap

  }


  @Test
  def question01(): Unit = {
    student
      .map(s => s.gender)
      .groupBy(g => g)
      .map(kv => s"${kv._1},${kv._2.size}")
      .foreach(println)
  }

  @Test
  def question02(): Unit = {
    score
      .map(s => (s.id, s.sco))
      .groupBy(_._1)
      .map(kv => {
        val id: String = kv._1
        val scoList: List[Int] = kv._2.map(_._2)
        (id, scoList.sum / scoList.size.toDouble)
      }).foreach(println)

    // 如果是按百分制计算平均分
    val subjectFullScoMap: Map[String, Int] = subject
      .map(s => (s.subjectId, s.fullSco))
      .toMap
    score
      .map(s => {
        val fullSco: Int = subjectFullScoMap.getOrElse(s.subjectId, 0)
        (s.id, s.sco.toDouble * 100 / fullSco)
      })
      .groupBy(_._1)
      .map(kv => {
        val id: String = kv._1
        val scoList: List[Double] = kv._2.map(_._2)
        s"$id,${scoList.sum / scoList.size}"
      }).foreach(println)
  }

  @Test
  // 统计每个学生的总分 [学号,学生姓名,学生年龄,总分]
  def question03(): Unit = {
    // 计算每个人的总分
    //    val idSumScoMap: Map[String, Int] = score
    //      .groupBy(_.id)
    //      // 如果groupBy之后的map方法中返回的每个元素是2元组，则整体会形成一个Map
    //      .map(kv => (kv._1, kv._2.map(_.sco).sum))


    // 遍历学生的数据，通过Map进行关联，获取每个学生的总分
    student
      .map(stu => {
        val sumSco: Int = sumScoreMap.getOrElse(stu.id, 0)
        s"${stu.id},${stu.name},${stu.age},$sumSco"
      })
      .foreach(println)
  }

  def filterJoinAndPrintWithIds(ids: List[String]): Unit = {
    // 先构建两个Map
    // 1、以学生id作为Key，姓名+班级作为Value stuNameAndClazzMap
    // 2、以科目id作为Key，科目名作为Value subjectNameMap
    //    val stuNameAndClazzMap: Map[String, String] = student.map(stu => (stu.id, s"${stu.name},${stu.clazz}")).toMap
    val subjectNameMap: Map[String, String] = subject.map(sub => (sub.subjectId, sub.subjectName)).toMap

    // 遍历分数
    score
      // 将符合条件的学生分数过滤出来
      .filter(sco => ids.contains(sco.id))
      // 借助两个Map实现关联
      .map(sco => {
        val id: String = sco.id
        val subjectId: String = sco.subjectId
        val nameAndClazz: String = stuNameAndClazzMap.getOrElse(id, "")
        val subjectName: String = subjectNameMap.getOrElse(subjectId, "")
        s"$id,$nameAndClazz,$subjectName,${sco.sco}"
      })
      .foreach(println)
  }

  @Test
  // 1、统计年级排名前十学生各科的分数 [学号,  姓名，班级，科目，分数]
  def question1(): Unit = {
    val ids: List[String] = sumScoreMap
      .toList
      .sortBy(-_._2)
      // 取前10条数据
      .take(10)
      // 取出id
      .map(_._1)

    filterJoinAndPrintWithIds(ids)

  }

  @Test
  // 2、统计总分大于年级平均分的学生 [学号，姓名，班级，总分]
  def question2(): Unit = {
    val avgSco: Double = sumScoreMap.values.sum / sumScoreMap.size.toDouble
    println(avgSco) // 年级平均分

    sumScoreMap
      .filter(kv => kv._2 > avgSco)
      .map(kv => {
        val id: String = kv._1
        val nameAndClazz: String = stuNameAndClazzMap.getOrElse(id, "")
        s"$id,$nameAndClazz,${kv._2}"
      })
      .foreach(println)

  }

  @Test
  // 3、统计每科都及格的学生 [学号，姓名，班级，科目，分数]
  def question3(): Unit = {

    // 遍历分数数据
    val allPassIds: List[String] = score
      // 将不及格的分数过滤掉，及格的保留
      .filter(sco => {
        val fullSco: Int = subjectFullScoMap.getOrElse(sco.subjectId, 1000000000)
        sco.sco.toDouble / fullSco >= 0.6
      })
      // 统计每个学生及格的科目数量
      .groupBy(_.id)
      .map(kv => (kv._1, kv._2.size))
      // 过滤出科目都及格的学生
      .filter(_._2 == 6)
      .keys
      .toList

    filterJoinAndPrintWithIds(allPassIds)

  }

  @Test
  // 统计每个班级的前三名 [学号，姓名，班级，分数]
  def question04(): Unit = {
    sumScoreMap
      .map(kv => {
        val nameAndClazz: String = stuNameAndClazzMap.getOrElse(kv._1, "")
        val splits: Array[String] = nameAndClazz.split(",")
        (kv._1, splits(0), splits(1), kv._2)
      })
      .groupBy(_._3)
      .flatMap(kv => {
        kv._2
          .toList
          .sortBy(-_._4)
          .take(3)
      }).foreach(println)
  }

  @Test
  // 5、统计偏科最严重的前100名学生  [学号，姓名，班级，科目，分数]
  def question05(): Unit = {
    val idList: List[String] = score
      .groupBy(_.id)
      .map(kv => {
        val newScoList: List[Double] = kv
          ._2
          .map(s => {
            s.sco * 100.toDouble / subjectFullScoMap.getOrElse(s.subjectId, 100)
          })
        val avgSco: Double = newScoList.sum / newScoList.size
        (kv._1, newScoList.map(newSco => Math.pow(newSco - avgSco, 2)).sum)
      })
      .toList
      .sortBy(-_._2)
      .map(_._1)
      .take(100)

    filterJoinAndPrintWithIds(idList)
  }

}
